Skip to content

Conversation

@fmayer
Copy link
Contributor

@fmayer fmayer commented Mar 21, 2025

This is used e.g. for iterators.

Created using spr 1.3.4
@fmayer fmayer changed the title [analyzer] Add matcher for pointer-like types (e.g. iterators) [clang][dataflow] Add matcher for pointer-like types to be cached Mar 21, 2025
@fmayer fmayer requested a review from jvoung March 21, 2025 00:23
@fmayer fmayer marked this pull request as ready for review March 21, 2025 00:23
@llvmbot llvmbot added clang Clang issues not falling into any other category clang:dataflow Clang Dataflow Analysis framework - https://clang.llvm.org/docs/DataFlowAnalysisIntro.html clang:analysis labels Mar 21, 2025
@llvmbot
Copy link
Member

llvmbot commented Mar 21, 2025

@llvm/pr-subscribers-clang-analysis

@llvm/pr-subscribers-clang

Author: Florian Mayer (fmayer)

Changes

This is used e.g. for iterators.


Full diff: https://github.com/llvm/llvm-project/pull/132314.diff

3 Files Affected:

  • (modified) clang/include/clang/Analysis/FlowSensitive/SmartPointerAccessorCaching.h (+2)
  • (modified) clang/lib/Analysis/FlowSensitive/SmartPointerAccessorCaching.cpp (+24)
  • (modified) clang/unittests/Analysis/FlowSensitive/SmartPointerAccessorCachingTest.cpp (+106)
diff --git a/clang/include/clang/Analysis/FlowSensitive/SmartPointerAccessorCaching.h b/clang/include/clang/Analysis/FlowSensitive/SmartPointerAccessorCaching.h
index 1b116a0cf76ed..b4291347e0969 100644
--- a/clang/include/clang/Analysis/FlowSensitive/SmartPointerAccessorCaching.h
+++ b/clang/include/clang/Analysis/FlowSensitive/SmartPointerAccessorCaching.h
@@ -58,7 +58,9 @@ namespace clang::dataflow {
 /// for `std::optional`, we assume the (Matcher, TransferFunction) case
 /// with custom handling is ordered early so that these generic cases
 /// do not trigger.
+ast_matchers::StatementMatcher isPointerLikeOperatorStar();
 ast_matchers::StatementMatcher isSmartPointerLikeOperatorStar();
+ast_matchers::StatementMatcher isPointerLikeOperatorArrow();
 ast_matchers::StatementMatcher isSmartPointerLikeOperatorArrow();
 ast_matchers::StatementMatcher isSmartPointerLikeValueMethodCall();
 ast_matchers::StatementMatcher isSmartPointerLikeGetMethodCall();
diff --git a/clang/lib/Analysis/FlowSensitive/SmartPointerAccessorCaching.cpp b/clang/lib/Analysis/FlowSensitive/SmartPointerAccessorCaching.cpp
index 4f5d18225c414..0860cc1dbaf8e 100644
--- a/clang/lib/Analysis/FlowSensitive/SmartPointerAccessorCaching.cpp
+++ b/clang/lib/Analysis/FlowSensitive/SmartPointerAccessorCaching.cpp
@@ -120,6 +120,14 @@ AST_MATCHER(clang::CXXRecordDecl, smartPointerClassWithGetOrValue) {
   return HasStarAndArrow && (HasGet || HasValue);
 }
 
+AST_MATCHER(clang::CXXRecordDecl, pointerClass) {
+  bool HasGet = false;
+  bool HasValue = false;
+  bool HasStarAndArrow =
+      clang::dataflow::hasSmartPointerClassShape(Node, HasGet, HasValue);
+  return HasStarAndArrow;
+}
+
 } // namespace
 
 namespace clang::dataflow {
@@ -140,6 +148,22 @@ ast_matchers::StatementMatcher isSmartPointerLikeOperatorArrow() {
                            ofClass(smartPointerClassWithGetOrValue()))));
 }
 
+ast_matchers::StatementMatcher isPointerLikeOperatorStar() {
+  return cxxOperatorCallExpr(
+      hasOverloadedOperatorName("*"),
+      callee(cxxMethodDecl(parameterCountIs(0),
+                           returns(hasCanonicalType(referenceType())),
+                           ofClass(pointerClass()))));
+}
+
+ast_matchers::StatementMatcher isPointerLikeOperatorArrow() {
+  return cxxOperatorCallExpr(
+      hasOverloadedOperatorName("->"),
+      callee(cxxMethodDecl(parameterCountIs(0),
+                           returns(hasCanonicalType(pointerType())),
+                           ofClass(pointerClass()))));
+}
+
 ast_matchers::StatementMatcher isSmartPointerLikeValueMethodCall() {
   return cxxMemberCallExpr(callee(cxxMethodDecl(
       parameterCountIs(0), returns(hasCanonicalType(referenceType())),
diff --git a/clang/unittests/Analysis/FlowSensitive/SmartPointerAccessorCachingTest.cpp b/clang/unittests/Analysis/FlowSensitive/SmartPointerAccessorCachingTest.cpp
index 18b9f80e32bbf..0f7477d875960 100644
--- a/clang/unittests/Analysis/FlowSensitive/SmartPointerAccessorCachingTest.cpp
+++ b/clang/unittests/Analysis/FlowSensitive/SmartPointerAccessorCachingTest.cpp
@@ -45,15 +45,62 @@ TEST(SmartPointerAccessorCachingTest, MatchesClassWithStarArrowGet) {
   EXPECT_TRUE(matches(Decls,
                       "int target(std::unique_ptr<S> P) { return (*P).i; }",
                       isSmartPointerLikeOperatorStar()));
+  EXPECT_TRUE(matches(Decls,
+                      "int target(std::unique_ptr<S> P) { return (*P).i; }",
+                      isPointerLikeOperatorStar()));
+
   EXPECT_TRUE(matches(Decls,
                       "int target(std::unique_ptr<S> P) { return P->i; }",
                       isSmartPointerLikeOperatorArrow()));
+  EXPECT_TRUE(matches(Decls,
+                      "int target(std::unique_ptr<S> P) { return P->i; }",
+                      isPointerLikeOperatorArrow()));
+
   EXPECT_TRUE(matches(Decls,
                       "int target(std::unique_ptr<S> P) { return P.get()->i; }",
                       isSmartPointerLikeGetMethodCall()));
 
   EXPECT_TRUE(matches(Decls, "int target(UniquePtrAlias<S> P) { return P->i; }",
                       isSmartPointerLikeOperatorArrow()));
+  EXPECT_TRUE(matches(Decls, "int target(UniquePtrAlias<S> P) { return P->i; }",
+                      isPointerLikeOperatorArrow()));
+}
+
+TEST(SmartPointerAccessorCachingTest, MatchesClassWithStarArrow) {
+  llvm::StringRef Decls(R"cc(
+    namespace std {
+    template <class T>
+    struct unique_ptr {
+      T* operator->() const;
+      T& operator*() const;
+    };
+    }  // namespace std
+
+    template <class T>
+    using UniquePtrAlias = std::unique_ptr<T>;
+
+    struct S { int i; };
+  )cc");
+
+  EXPECT_FALSE(matches(Decls,
+                       "int target(std::unique_ptr<S> P) { return (*P).i; }",
+                       isSmartPointerLikeOperatorStar()));
+  EXPECT_TRUE(matches(Decls,
+                      "int target(std::unique_ptr<S> P) { return (*P).i; }",
+                      isPointerLikeOperatorStar()));
+
+  EXPECT_FALSE(matches(Decls,
+                       "int target(std::unique_ptr<S> P) { return P->i; }",
+                       isSmartPointerLikeOperatorArrow()));
+  EXPECT_TRUE(matches(Decls,
+                      "int target(std::unique_ptr<S> P) { return P->i; }",
+                      isPointerLikeOperatorArrow()));
+
+  EXPECT_FALSE(matches(Decls,
+                       "int target(UniquePtrAlias<S> P) { return P->i; }",
+                       isSmartPointerLikeOperatorArrow()));
+  EXPECT_TRUE(matches(Decls, "int target(UniquePtrAlias<S> P) { return P->i; }",
+                      isPointerLikeOperatorArrow()));
 }
 
 TEST(SmartPointerAccessorCachingTest, NoMatchIfUnexpectedReturnTypes) {
@@ -75,15 +122,25 @@ TEST(SmartPointerAccessorCachingTest, NoMatchIfUnexpectedReturnTypes) {
   EXPECT_FALSE(matches(Decls,
                        "int target(std::unique_ptr<S, T> P) { return (*P).i; }",
                        isSmartPointerLikeOperatorStar()));
+  EXPECT_FALSE(matches(Decls,
+                       "int target(std::unique_ptr<S, T> P) { return (*P).i; }",
+                       isPointerLikeOperatorStar()));
+
   EXPECT_FALSE(matches(Decls,
                        "int target(std::unique_ptr<S, T> P) { return P->j; }",
                        isSmartPointerLikeOperatorArrow()));
+  EXPECT_FALSE(matches(Decls,
+                       "int target(std::unique_ptr<S, T> P) { return P->j; }",
+                       isPointerLikeOperatorArrow()));
   // The class matching arguably accidentally matches, just because the
   // instantiation is with S, S. Hopefully doesn't happen too much in real code
   // with such operator* and operator-> overloads.
   EXPECT_TRUE(matches(Decls,
                       "int target(std::unique_ptr<S, S> P) { return P->i; }",
                       isSmartPointerLikeOperatorArrow()));
+  EXPECT_TRUE(matches(Decls,
+                      "int target(std::unique_ptr<S, S> P) { return P->i; }",
+                      isPointerLikeOperatorArrow()));
 }
 
 TEST(SmartPointerAccessorCachingTest, NoMatchIfBinaryStar) {
@@ -103,6 +160,9 @@ TEST(SmartPointerAccessorCachingTest, NoMatchIfBinaryStar) {
   EXPECT_FALSE(
       matches(Decls, "int target(std::unique_ptr<S> P) { return (P * 10).i; }",
               isSmartPointerLikeOperatorStar()));
+  EXPECT_FALSE(
+      matches(Decls, "int target(std::unique_ptr<S> P) { return (P * 10).i; }",
+              isPointerLikeOperatorStar()));
 }
 
 TEST(SmartPointerAccessorCachingTest, NoMatchIfNoConstOverloads) {
@@ -122,9 +182,17 @@ TEST(SmartPointerAccessorCachingTest, NoMatchIfNoConstOverloads) {
   EXPECT_FALSE(matches(Decls,
                        "int target(std::unique_ptr<S> P) { return (*P).i; }",
                        isSmartPointerLikeOperatorStar()));
+  EXPECT_FALSE(matches(Decls,
+                       "int target(std::unique_ptr<S> P) { return (*P).i; }",
+                       isPointerLikeOperatorStar()));
+
   EXPECT_FALSE(matches(Decls,
                        "int target(std::unique_ptr<S> P) { return P->i; }",
                        isSmartPointerLikeOperatorArrow()));
+  EXPECT_FALSE(matches(Decls,
+                       "int target(std::unique_ptr<S> P) { return P->i; }",
+                       isPointerLikeOperatorArrow()));
+
   EXPECT_FALSE(
       matches(Decls, "int target(std::unique_ptr<S> P) { return P.get()->i; }",
               isSmartPointerLikeGetMethodCall()));
@@ -146,6 +214,10 @@ TEST(SmartPointerAccessorCachingTest, NoMatchIfNoStarMethod) {
   EXPECT_FALSE(matches(Decls,
                        "int target(std::unique_ptr<S> P) { return P->i; }",
                        isSmartPointerLikeOperatorArrow()));
+  EXPECT_FALSE(matches(Decls,
+                       "int target(std::unique_ptr<S> P) { return P->i; }",
+                       isPointerLikeOperatorArrow()));
+
   EXPECT_FALSE(matches(Decls,
                        "int target(std::unique_ptr<S> P) { return P->i; }",
                        isSmartPointerLikeGetMethodCall()));
@@ -171,15 +243,31 @@ TEST(SmartPointerAccessorCachingTest, MatchesWithValueAndNonConstOverloads) {
   EXPECT_TRUE(matches(
       Decls, "int target(std::optional<S> &NonConst) { return (*NonConst).i; }",
       isSmartPointerLikeOperatorStar()));
+  EXPECT_TRUE(matches(
+      Decls, "int target(std::optional<S> &NonConst) { return (*NonConst).i; }",
+      isPointerLikeOperatorStar()));
+
   EXPECT_TRUE(matches(
       Decls, "int target(const std::optional<S> &Const) { return (*Const).i; }",
       isSmartPointerLikeOperatorStar()));
+  EXPECT_TRUE(matches(
+      Decls, "int target(const std::optional<S> &Const) { return (*Const).i; }",
+      isPointerLikeOperatorStar()));
+
   EXPECT_TRUE(matches(
       Decls, "int target(std::optional<S> &NonConst) { return NonConst->i; }",
       isSmartPointerLikeOperatorArrow()));
+  EXPECT_TRUE(matches(
+      Decls, "int target(std::optional<S> &NonConst) { return NonConst->i; }",
+      isPointerLikeOperatorArrow()));
+
   EXPECT_TRUE(matches(
       Decls, "int target(const std::optional<S> &Const) { return Const->i; }",
       isSmartPointerLikeOperatorArrow()));
+  EXPECT_TRUE(matches(
+      Decls, "int target(const std::optional<S> &Const) { return Const->i; }",
+      isPointerLikeOperatorArrow()));
+
   EXPECT_TRUE(matches(
       Decls,
       "int target(std::optional<S> &NonConst) { return NonConst.value().i; }",
@@ -214,16 +302,34 @@ TEST(SmartPointerAccessorCachingTest, MatchesWithTypeAliases) {
       Decls,
       "int target(HasGetAndValue<S> &NonConst) { return (*NonConst).i; }",
       isSmartPointerLikeOperatorStar()));
+  EXPECT_TRUE(matches(
+      Decls,
+      "int target(HasGetAndValue<S> &NonConst) { return (*NonConst).i; }",
+      isPointerLikeOperatorStar()));
+
   EXPECT_TRUE(matches(
       Decls,
       "int target(const HasGetAndValue<S> &Const) { return (*Const).i; }",
       isSmartPointerLikeOperatorStar()));
+  EXPECT_TRUE(matches(
+      Decls,
+      "int target(const HasGetAndValue<S> &Const) { return (*Const).i; }",
+      isPointerLikeOperatorStar()));
+
   EXPECT_TRUE(matches(
       Decls, "int target(HasGetAndValue<S> &NonConst) { return NonConst->i; }",
       isSmartPointerLikeOperatorArrow()));
+  EXPECT_TRUE(matches(
+      Decls, "int target(HasGetAndValue<S> &NonConst) { return NonConst->i; }",
+      isPointerLikeOperatorArrow()));
+
   EXPECT_TRUE(matches(
       Decls, "int target(const HasGetAndValue<S> &Const) { return Const->i; }",
       isSmartPointerLikeOperatorArrow()));
+  EXPECT_TRUE(matches(
+      Decls, "int target(const HasGetAndValue<S> &Const) { return Const->i; }",
+      isPointerLikeOperatorArrow()));
+
   EXPECT_TRUE(matches(
       Decls,
       "int target(HasGetAndValue<S> &NonConst) { return NonConst.value().i; }",

@fmayer fmayer merged commit 1cbcaa4 into main Mar 21, 2025
17 checks passed
@fmayer fmayer deleted the users/fmayer/spr/analyzer-add-matcher-for-pointer-like-types-eg-iterators branch March 21, 2025 17:27
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

clang:analysis clang:dataflow Clang Dataflow Analysis framework - https://clang.llvm.org/docs/DataFlowAnalysisIntro.html clang Clang issues not falling into any other category

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants